package com.photoeditor.demo.util.gl;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;

import com.android.gl.Geometry3D;
import com.android.gl.Object3D;
import com.android.gl.loader.LoaderOBJ;
import com.android.gl.loader.ParsingException;
import com.android.gl.materials.Material;
import com.android.gl.materials.plugins.IMaterialPlugin;
import com.android.gl.materials.textures.ATexture;
import com.android.gl.materials.textures.AlphaMapTexture;
import com.android.gl.materials.textures.StreamingTexture;
import com.android.gl.materials.textures.Texture;
import com.android.gl.math.vector.Vector3;
import com.android.gl.primitives.Cube;
import com.android.gl.primitives.Plane;
import com.android.gl.primitives.Sphere;
import com.android.gl.renderer.Renderer;
import com.android.gl.util.ObjectColorPicker;
import com.android.gl.util.OnObjectPickedListener;
import com.photoeditor.demo.util.gl.model.DynamicPoint;
import com.photoeditor.demo.util.gl.model.Ornament;
import com.photoeditor.demo.util.gl.util.BitmapUtils;
import com.photoeditor.demo.util.gl.util.OrnamentFactory;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Simon on 2017/7/19.
 */

public class My3DRenderer extends Renderer implements OnObjectPickedListener, StreamingTexture.ISurfaceListener {
    private final static String TAG = My3DRenderer.class.getSimpleName();

    private Object3D mContainer;
    private List<Object3D> mObject3DList = new ArrayList<>();
    private Object3D mPickedObject;
    private Object3D mShaderPlane;
    private Material mCustomMaterial;
    private MyFragmentShader mMyFragmentShader;

    private String mTextureName;
    private Ornament mOrnamentModel;
    private boolean mIsNeedUpdateOrnament = false;
    private boolean mIsOrnamentVisible = true;
    private int mScreenW = 1;
    private int mScreenH = 1;
    // 根据肤色更改模型贴图的颜色
    private int mSkinColor = 0xffd4c9b5;

    private int mModelType = Ornament.MODEL_TYPE_NONE;
    // 用于静态3D模型
    private Vector3 mAccValues;
    private float mTransX = 0.0f;
    private float mTransY = 0.0f;
    private float mScale = 1.0f;
    // 用于动态3D模型
    private List<Geometry3D> mGeometry3DList = new ArrayList<>();
    private List<DynamicPoint> mPoints = new ArrayList<>();
    private boolean mIsChanging = false;
    private boolean mIsOrnamentChange = false;
    private boolean mIsNextFrame = false;
    private OrnamentChangeListener mOrnamentChangeListener;
    // 用于ShaderMaterial模型
    private List<Material> mMaterialList = new ArrayList<>();
    private float mMaterialTime = 0;
    private ObjectColorPicker mPicker;

    // StreamingTexture
    private Surface mSurface;
    private View mStreamingView;
    private Handler mStreamingHandler;
    private StreamingTexture mStreamingTexture;
    private volatile boolean mShouldUpdateTexture;
    private final float[] mMatrix = new float[16];
    private boolean mIsStreamingViewMirror = false;

    private final Runnable mUpdateTexture = new Runnable() {
        public void run() {
            // -- Draw the view on the canvas
            if (mSurface != null && mStreamingTexture != null && mStreamingView != null) {
                try {
                    final Canvas canvas = mSurface.lockCanvas(null);
                    canvas.translate(mStreamingView.getScrollX(), -mStreamingView.getScrollY());
                    if (mIsStreamingViewMirror) {
                        // 镜像
                        canvas.scale(-1, 1, mStreamingView.getWidth() / 2, mStreamingView.getHeight() / 2);
                    }
                    mStreamingTexture.getSurfaceTexture().getTransformMatrix(mMatrix);
                    mStreamingView.draw(canvas);
                    mSurface.unlockCanvasAndPost(canvas);
                    // -- Indicates that the texture should be updated on the OpenGL thread.
                    mShouldUpdateTexture = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    };


    public My3DRenderer(Context context) {
        super(context);
        mAccValues = new Vector3();
    }

    public void setOrnamentModel(Ornament mOrnamentModel) {
        this.mOrnamentModel = mOrnamentModel;
    }

    public void setIsNeedUpdateOrnament(boolean mIsNeedUpdateOrnament) {
        this.mIsNeedUpdateOrnament = mIsNeedUpdateOrnament;
    }

    public void setTextureName(String name) {
        mTextureName = name;
    }

    // 设置装饰品可见性
    public void setIsOrnamentVisible(boolean mIsOrnamentVisible) {
        this.mIsOrnamentVisible = mIsOrnamentVisible;
    }

    // 设置3D模型的转动角度
    public void setAccelerometerValues(float x, float y, float z) {
        mAccValues.setAll(x, y, z);
    }

    // 设置3D模型的平移
    public void setTransition(float x, float y, float z) {
        if (mModelType == Ornament.MODEL_TYPE_STATIC || mModelType == Ornament.MODEL_TYPE_SHADER) {
            mTransX = x;
            mTransY = y;
            setScale(z);
        }
    }

    // 设置3D模型的缩放比例
    public void setScale(float scale) {
        mScale = scale;
    }

    public void setScreenW(int width) {
        mScreenW = width;
    }

    public void setScreenH(int height) {
        mScreenH = height;
    }

    public void setSkinColor(int mSkinColor) {
        this.mSkinColor = mSkinColor;
    }

    public void setStreamingView(View streamingView) {
        this.mStreamingView = streamingView;
    }

    public void setStreamingHandler(Handler streamingHandler) {
        this.mStreamingHandler = streamingHandler;
    }

    @Override
    protected void initScene() {
        try {
            mContainer = new Object3D();
            getCurrentScene().addChild(mContainer);
            getCurrentScene().getCamera().setZ(5.5);
        } catch (Exception e) {
            e.printStackTrace();
        }

        getCurrentScene().setBackgroundColor(0);
    }

    @Override
    protected void onRender(long ellapsedRealtime, double deltaTime) {
        super.onRender(ellapsedRealtime, deltaTime);

        if (mIsNeedUpdateOrnament) {
            mIsNeedUpdateOrnament = false;
            loadOrnament();
        }

        if (mModelType == Ornament.MODEL_TYPE_STATIC || mModelType == Ornament.MODEL_TYPE_SHADER) {
            if (mOrnamentModel != null) {
                if (mOrnamentModel.isEnableRotation()) {
                    // 处理3D模型的旋转
                    mContainer.setRotation(mAccValues.x, mAccValues.y, mAccValues.z);
                }

                if (mOrnamentModel.isEnableScale()) {
                    // 处理3D模型的缩放
                    mContainer.setScale(mScale);
                }

                if (mOrnamentModel.isEnableTransition()) {
                    // 处理3D模型的平移
                    getCurrentCamera().setX(mTransX);
                    getCurrentCamera().setY(mTransY);
                }
            }

            if (mOrnamentModel != null && mOrnamentModel.getTimeStep() > 0 && mMaterialList != null) {
                for (int i = 0; i < mMaterialList.size(); i++) {
                    Material material = mMaterialList.get(i);
                    if (material != null) {
                        material.setTime(mMaterialTime);
                        mMaterialTime += mOrnamentModel.getTimeStep();
                        if (mMaterialTime > 1000) {
                            mMaterialTime = 0;
                        }
                    }
                }
            }

        } else if (mModelType == Ornament.MODEL_TYPE_DYNAMIC) {
            if (!mIsChanging && mPoints != null && mPoints.size() > 0) {
                mIsChanging = true;

                try {  // FIXME
                    if (mGeometry3DList != null && mGeometry3DList.size() > 0) {
                        for (Geometry3D geometry3D : mGeometry3DList) {
                            FloatBuffer vertBuffer = geometry3D.getVertices();
                            for (int i = 0; i < mPoints.size(); i++) {
                                DynamicPoint point = mPoints.get(i);
                                if (mOrnamentModel != null && mOrnamentModel.isUseGvPt()) {
                                    changeGvPoint(vertBuffer, point.getIndex(), point.getX(), point.getY(), point.getZ());
                                } else {
                                    changePoint(vertBuffer, point.getIndex(), point.getX(), point.getY(), point.getZ());
                                }
                            }
                            geometry3D.changeBufferData(geometry3D.getVertexBufferInfo(), vertBuffer, 0, vertBuffer.limit());
                        }
                    }

                    if (mIsOrnamentChange) {//添加了模型或模型发生了变换
                        if (!mIsNextFrame) {//记录状态，下一帧将调用监听者
                            mIsNextFrame = true;
                        } else {
                            //下一帧，关闭标志位，调用监听器
                            mIsNextFrame = false;
                            mIsOrnamentChange = false;
                            if (mOrnamentChangeListener != null) {
                                mOrnamentChangeListener.onOrnamentChange();
                            }
                        }
                    }

                } catch (Exception e) {
                    Log.e(TAG, e.toString());
                }

                mIsChanging = false;
            }
        }

        // TODO
        if (mShaderPlane != null && mOrnamentModel != null && mMyFragmentShader != null && mCustomMaterial != null) {
            mMyFragmentShader.setScreenW(mScreenW);
            mMyFragmentShader.setScreenH(mScreenH);

            if (mMaterialTime == 0) {
                mMyFragmentShader.setFlag(1);
            }

            mMaterialTime += mOrnamentModel.getTimeStep();
            mCustomMaterial.setTime(mMaterialTime);

            if (mMaterialTime > mOrnamentModel.getTimePeriod()) {
                mMyFragmentShader.setFlag(0);
            }

            if (mMaterialTime > 1) {
                mMaterialTime = 0;
            }
        }

        // -- not a really accurate way of doing things but you get the point :)
        if (mSurface != null && mStreamingHandler != null && mFrameCount++ >= (mFrameRate * 0.25)) {
            mFrameCount = 0;
            mStreamingHandler.post(mUpdateTexture);
        }
        // -- update the texture because it is ready
        if (mShouldUpdateTexture) {
            if (mStreamingTexture != null) {
                mStreamingTexture.update();
            }
            mShouldUpdateTexture = false;
        }

        if (mPickedObject != null && mOrnamentModel != null && mObject3DList != null && mObject3DList.size() > 0) {
            int index = mObject3DList.indexOf(mPickedObject);
            if (index >= 0) {
                List<Ornament.Model> modelList = mOrnamentModel.getModelList();
                if (modelList != null && modelList.size() > index) {
                    Ornament.Model model = modelList.get(index);
                    if (model != null && model.isNeedObjectPick()) {
                        boolean isPicked = model.isPicked();
                        if (isPicked) {
                            mPickedObject.setPosition(model.getAfterX(), model.getAfterY(), model.getAfterZ());
                            mPickedObject.setRotation(model.getAxisX(), model.getAxisY(), model.getAxisZ(),
                                    model.getAfterAngle());
                        } else {
                            mPickedObject.setPosition(model.getBeforeX(), model.getBeforeY(), model.getBeforeZ());
                            mPickedObject.setRotation(model.getAxisX(), model.getAxisY(), model.getAxisZ(),
                                    model.getBeforeAngle());
                        }
                    }
                }
            }
        }
    }

    @Override
    public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) {
    }

    @Override
    public void onTouchEvent(MotionEvent event) {
    }

    @Override
    public void onObjectPicked(@NonNull Object3D object) {
        Log.i(TAG, "onObjectPicked: " + object.getName());
        mPickedObject = object;
        if (mOrnamentModel != null && mObject3DList != null && mObject3DList.size() > 0) {
            int index = mObject3DList.indexOf(object);
            if (index >= 0) {
                List<Ornament.Model> modelList = mOrnamentModel.getModelList();
                if (modelList != null && modelList.size() > index) {
                    Ornament.Model model = modelList.get(index);
                    if (model != null && model.isNeedObjectPick()) {
                        boolean isPicked = model.isPicked();
                        isPicked = !isPicked;
                        model.setPicked(isPicked);
                    }
                }
            }
        }
    }

    @Override
    public void onNoObjectPicked() {
    }

    @Override
    public void setSurface(Surface surface) {
        mSurface = surface;
        if (mStreamingTexture != null && mStreamingView != null) {
            mStreamingTexture.getSurfaceTexture().setDefaultBufferSize(mStreamingView.getWidth(), mStreamingView.getHeight());
        }
    }

    private void clearScene() {
        if (mObject3DList != null && mObject3DList.size() > 0) {
            for (int i = 0; i < mObject3DList.size(); i++) {
                Object3D object3D = mObject3DList.get(i);
                if (object3D != null) {
                    if (mPicker != null) {
                        mPicker.unregisterObject(object3D);
                    }
                    mContainer.removeChild(object3D);
                    object3D.destroy();
                }
            }
            mObject3DList.clear();
        }

        mPicker = null;
        mPickedObject = null;

        if (mMaterialList != null && mMaterialList.size() > 0) {
            mMaterialList.clear();
        }

        if (mGeometry3DList != null && mGeometry3DList.size() > 0) {
            mGeometry3DList.clear();
        }

        if (mShaderPlane != null) {
            mContainer.removeChild(mShaderPlane);
            mShaderPlane = null;
        }

        mIsStreamingViewMirror = false;
        mStreamingTexture = null;

        mMaterialTime = 0;
    }

    private void loadOrnament() {
        try {
            clearScene();

            if (mOrnamentModel != null) {
                mModelType = mOrnamentModel.getType();
                switch (mModelType) {
                    case Ornament.MODEL_TYPE_SHADER:
                        loadShaderMaterialModel();
                        break;
                    case Ornament.MODEL_TYPE_STATIC:
                    case Ornament.MODEL_TYPE_DYNAMIC:
                        loadNormalMaterialModel();
                        initOrnamentParams();
                        break;
                }

                boolean isHasShaderPlane = mOrnamentModel.isHasShaderPlane();
                if (isHasShaderPlane) {
                    loadShaderPlane();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void loadShaderMaterialModel() {
        try {
            List<Object3D> object3DList = mOrnamentModel.getObject3DList();
            List<List<IMaterialPlugin>> materialList = mOrnamentModel.getMaterialList();
            if (object3DList != null && materialList != null) {
                mObject3DList.addAll(object3DList);

                for (List<IMaterialPlugin> pluginList : materialList) {
                    Material material = new Material();
                    material.enableTime(true);
                    for (IMaterialPlugin plugin : pluginList) {
                        material.addPlugin(plugin);
                    }
                    mMaterialList.add(material);
                }

                if (mObject3DList != null && mObject3DList.size() > 0) {
                    for (int i = 0; i < mObject3DList.size(); i++) {
                        Object3D object3D = mObject3DList.get(i);
                        if (object3D != null) {
                            Material material = mMaterialList.get(i);
                            if (material != null) {
                                object3D.setMaterial(material);
                            }
                            mContainer.addChild(object3D);
                        }
                    }

                    mContainer.setScale(mOrnamentModel.getScale());
                    mContainer.setPosition(mOrnamentModel.getOffsetX(), mOrnamentModel.getOffsetY(), mOrnamentModel.getOffsetZ());
                    mContainer.setRotation(mOrnamentModel.getRotateX(), mOrnamentModel.getRotateY(), mOrnamentModel.getRotateZ());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void loadNormalMaterialModel() {
        try {
            List<Ornament.Model> modelList = mOrnamentModel.getModelList();
            if (modelList != null && modelList.size() > 0) {
                for (Ornament.Model model : modelList) {
                    String texturePath = model.getTexturePath();

                    Object3D object3D;
                    if (texturePath != null) {
                        object3D = loadDynamicModel(model);
                    } else {
                        object3D = loadStaticModel(model);
                    }

                    //不使用视图、投影矩阵
                    object3D.setUseProjection(false);

                    if (object3D != null) {
                        mObject3DList.add(object3D);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object3D loadStaticModel(Ornament.Model model) {
        try {
            Object3D object3D;
            int modelResId = model.getModelResId();
            int buildInType = model.getBuildInType();
            if (modelResId != -1) {
                object3D = getExternalModel(modelResId);
            } else if (buildInType != -1) {
                object3D = getBuildInModel(model, buildInType);
            } else {
                throw new RuntimeException("invalid object3d");
            }

            setModelBaseParams(model, object3D);
            setModelTexture(model, object3D);
            setModelMaterial(model, object3D);
            setModelColor(model, object3D);
            handleObjectPicking(model, object3D);
            handleStreamingTexture(model);

            return object3D;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private Object3D getExternalModel(int modelResId) throws ParsingException {
        LoaderOBJ objParser = new LoaderOBJ(mContext.getResources(), mTextureManager, modelResId, mTextureName);
        objParser.parse();
        return objParser.getParsedObject();
    }

    private Object3D getBuildInModel(Ornament.Model model, int buildInType) {
        switch (buildInType) {
            case Ornament.Model.BUILD_IN_PLANE:
                return new Plane(model.getBuildInWidth(), model.getBuildInHeight(),
                        model.getBuildInSegmentsW(), model.getBuildInSegmentsH());
            case Ornament.Model.BUILD_IN_CUBE:
                return new Cube(model.getBuildInWidth());
            case Ornament.Model.BUILD_IN_SPHERE:
                return new Sphere(model.getBuildInWidth(),
                        model.getBuildInSegmentsW(), model.getBuildInSegmentsH());
            default:
                throw new RuntimeException("invalid object3d");
        }
    }

    private void setModelBaseParams(Ornament.Model model, Object3D object3D) {
        String name = model.getName();
        object3D.setName(name == null ? "" : name);
        object3D.setScale(model.getScale());
        object3D.setPosition(model.getOffsetX(), model.getOffsetY(), model.getOffsetZ());
        object3D.setRotation(model.getRotateX(), model.getRotateY(), model.getRotateZ());
    }

    private void setModelTexture(Ornament.Model model, Object3D object3D) throws ATexture.TextureException {
        int textureResId = model.getTextureResId();
        if (textureResId > 0) {
            ATexture texture = object3D.getMaterial().getTextureList().get(0);
            object3D.getMaterial().removeTexture(texture);

            Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), textureResId);
            if (bitmap != null) {
                mIsChanging = true;
                // 调整肤色
                if (model.isNeedSkinColor()) {
                    bitmap = changeSkinColor(bitmap, mSkinColor);
                }
                object3D.getMaterial().addTexture(new Texture("canvas", bitmap));
                mIsChanging = false;
            }
        }
    }

    private void setModelMaterial(Ornament.Model model, Object3D object3D) {
        int materialId = model.getMaterialId();
        if (materialId > -1) {
//            object3D.setMaterial(MaterialFactory.getMaterialById(materialId));
        }
    }

    private void setModelColor(Ornament.Model model, Object3D object3D) {
        int color = model.getColor();
        if (color != OrnamentFactory.NO_COLOR) {
            object3D.getMaterial().setColor(color);
        }
    }

    private void handleObjectPicking(Ornament.Model model, Object3D object3D) {
        if (model.isNeedObjectPick()) {
            if (mPicker == null) {
                mPicker = new ObjectColorPicker(this);
                mPicker.setOnObjectPickedListener(this);
            }
            mPicker.registerObject(object3D);
        }
    }

    private void handleStreamingTexture(Ornament.Model model) throws ATexture.TextureException {
        if (model.isNeedStreaming()) {
            Object3D streamingModel;
            int modelType = model.getStreamingModelType();
            float modelWidth = model.getStreamingModelWidth();
            float modelHeight = model.getStreamingModelHeight();
            int modelSegmentsW = model.getStreamingModelSegmentsW();
            int modelSegmentsH = model.getStreamingModelSegmentsH();
            switch (modelType) {
                case Ornament.Model.STREAMING_PLANE_MODEL:
                    streamingModel = new Plane(modelWidth, modelHeight, modelSegmentsW, modelSegmentsH);
                    break;
                case Ornament.Model.STREAMING_SPHERE_MODEL:
                    streamingModel = new Sphere(modelWidth, modelSegmentsW, modelSegmentsH);
                    break;
                default:
                    throw new RuntimeException("invalid streaming model");
            }

            streamingModel.setTransparent(model.isStreamingModelTransparent());
            streamingModel.setColor(0);
            streamingModel.setScale(model.getScale());
            streamingModel.setPosition(model.getStreamingOffsetX(), model.getStreamingOffsetY(),model.getStreamingOffsetZ());
            streamingModel.setRotation(model.getStreamingRotateX(), model.getStreamingRotateY(), model.getStreamingRotateZ());
            streamingModel.setRenderChildrenAsBatch(true);
            if (mStreamingTexture == null) {
                mStreamingTexture = new StreamingTexture("viewTexture", this);
            }
            mStreamingTexture.setInfluence(model.getStreamingTextureInfluence());
            Material material = new Material();
            material.setColorInfluence(model.getColorInfluence());
            try {
                material.addTexture(mStreamingTexture);
            } catch (ATexture.TextureException e) {
                e.printStackTrace();
            }

            if (model.getAlphaMapResId() > 0) {
                material.addTexture(new AlphaMapTexture("alphaMapTex", model.getAlphaMapResId()));
            }
            streamingModel.setMaterial(material);
            mContainer.addChild(streamingModel);
            mObject3DList.add(streamingModel);

            mIsStreamingViewMirror = model.isStreamingViewMirror();
        }
    }

    private Object3D loadDynamicModel(Ornament.Model model) {
        try {
            String objDir = "OpenGLDemo/txt/";
            String objName = "base_face_uv3_obj";
            LoaderOBJ parser = new LoaderOBJ(this, objDir + objName);
            parser.parse();
            Object3D object3D = parser.getParsedObject();

            object3D.setScale(model.getScale());
            object3D.setPosition(model.getOffsetX(), model.getOffsetY(), model.getOffsetZ());
            object3D.setRotation(model.getRotateX(), model.getRotateY(), model.getRotateZ());

            ATexture texture = object3D.getMaterial().getTextureList().get(0);
            object3D.getMaterial().removeTexture(texture);

            String texturePath = model.getTexturePath();
            Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFilePath(texturePath, 300, 300);
            // 调整肤色
            if (model.isNeedSkinColor()) {
                bitmap = changeSkinColor(bitmap, mSkinColor);
            }
            object3D.getMaterial().addTexture(new Texture("canvas", bitmap));
            object3D.getMaterial().enableLighting(false);

            int color = model.getColor();
            if (color != OrnamentFactory.NO_COLOR) {
                object3D.getMaterial().setColor(color);
            }

            return object3D;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private void initOrnamentParams() {
        if (mObject3DList != null && mObject3DList.size() > 0) {
            for (Object3D object3D : mObject3DList) {
                mContainer.addChild(object3D);

                Geometry3D geometry3D = object3D.getGeometry();
                mGeometry3DList.add(geometry3D);
            }
        }

        mContainer.setTransparent(false);
        mContainer.setScale(1.0f);
        mContainer.setRotation(0, 0, 0);
        mContainer.setPosition(0, 0, 0);
        getCurrentCamera().setX(0);
        getCurrentCamera().setY(0);
    }

    private void loadShaderPlane() {
        int vertResId = mOrnamentModel.getVertResId();
        int fragResId = mOrnamentModel.getFragResId();
        if (vertResId > 0 && fragResId > 0) {
            mMyFragmentShader = new MyFragmentShader(fragResId);

            mCustomMaterial = new Material(
                    new MyVertexShader(vertResId),
                    mMyFragmentShader);
            mCustomMaterial.enableTime(true);

            float offsetX = mOrnamentModel.getPlaneOffsetX();
            float offsetY = mOrnamentModel.getPlaneOffsetY();
            float offsetZ = mOrnamentModel.getPlaneOffsetZ();
            mShaderPlane = new Plane(5, 5, 1, 1);
            mShaderPlane.setPosition(offsetX, offsetY, offsetZ);
            mShaderPlane.setMaterial(mCustomMaterial);
            mShaderPlane.setTransparent(true);
            mContainer.addChild(mShaderPlane);
        }
    }

    private int faceIndices[][]={
            {66, 68, 123, 125, 128, 132, 135, 137},
            {57, 59, 63, 64, 110, 114, 116, 120, 124},
            {51, 53, 67, 71, 86, 90, 92, 96, 121},
            {54, 56, 65, 69},
            {35, 39, 41, 45, 49, 52, 55, 58},
            {15, 70, 122, 129, 146, 152, 158},
            {17, 61, 126, 136, 150, 156, 162},
            {139, 144},
            {141, 142, 147, 149, 151, 154},
            {153, 155, 157, 160},
            {33, 34, 37, 99, 101, 105, 107},
            {100, 103},
            {36, 60, 97, 109},
            {112, 115},
            {16, 21, 24, 27, 30, 31, 62, 106, 118},
            {40, 43, 47, 75, 77, 81, 84},
            {76, 79},
            {44, 50, 83, 94},
            {88, 91},
            {2, 6, 9, 12, 13, 46, 72, 73, 85},
            {38, 42},
            {1, 4},
            {5, 7},
            {8, 10},
            {11, 14, 159},
            {18, 19, 161},
            {20, 22},
            {23, 25},
            {26, 28},
            {3, 48},
            {29, 32},
            {80, 82},
            {74, 78},
            {93, 95},
            {87, 89},
            {98, 102},
            {104, 108},
            {117, 119},
            {111, 113},
            {140, 145},
            {143, 148},
            {127, 130},
            {131, 133},
            {134, 138},
    };

    private int[] getIndexArrayByFace(int faceIndex) {
        return faceIndices[faceIndex];
    }

    private void changePoint(FloatBuffer vertBuffer, int faceIndex, float x, float y, float z) {
        int[] indices = getIndexArrayByFace(faceIndex);
        if (indices != null) {
            int len = indices.length;
            for (int i=0; i<len; i++) {
                int index = indices[i]-1;
                vertBuffer.put(index * 3, x);
                vertBuffer.put(index * 3 + 1, y);
                vertBuffer.put(index * 3 + 2, z);
            }
        }
    }

    private void changeGvPoint(FloatBuffer vertBuffer, int ptIndex, float x, float y, float z) {
        int[] ptIndexInVertexBuffer = gvFacePts[ptIndex];
        if (ptIndexInVertexBuffer != null && ptIndexInVertexBuffer.length > 0) {
            for (int i = 0; i < ptIndexInVertexBuffer.length; i++) {
                int index = ptIndexInVertexBuffer[i];
                vertBuffer.put(index * 3, x);
                vertBuffer.put(index * 3 + 1, y);
                vertBuffer.put(index * 3 + 2, z);
            }
        }
    }

    public void setDynamicPoints(List<DynamicPoint> mPoints) {
        if (!mIsChanging) {
            this.mPoints = mPoints;
        }
    }

    private Bitmap changeSkinColor(Bitmap bitmap, int skinColor) {
        if (bitmap != null) {
            Bitmap texture = bitmap.copy(Bitmap.Config.ARGB_8888, true);

            int width = texture.getWidth();
            int height = texture.getHeight();

            int skinRed = Color.red(skinColor);
            int skinGreen = Color.green(skinColor);
            int skinBlue = Color.blue(skinColor);

            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    int pixel = texture.getPixel(x, y);
                    int red = Color.red(pixel);
                    int green = Color.green(pixel);
                    int blue = Color.blue(pixel);

                    // TODO
                    // 将肤色与该点颜色进行混合
                    // 在Photoshop里面看，“柔光”的效果是比较合适的。 “叠加”也类似，不过画面有点过饱和
                    // 调色层在顶层并设为“柔光”，和人脸层在顶层并设为“柔光”是不同的
                    // 理想的效果是前者，但是在网上找到的“柔光”代码实现的是后者
                    // 由于没弄明白怎么改写，暂时先用“叠加”效果，然后降低饱和度
                    red = overlay(skinRed, red);
                    green = overlay(skinGreen, green);
                    blue = overlay(skinBlue, blue);

                    pixel = Color.rgb(red, green, blue);
                    texture.setPixel(x, y, pixel);
                }
            }

            // 降低饱和度
            float saturation = 0.35f;
            ColorMatrix cMatrix = new ColorMatrix();
            cMatrix.setSaturation(saturation);

            Paint paint = new Paint();
            paint.setColorFilter(new ColorMatrixColorFilter(cMatrix));

            Canvas canvas = new Canvas(texture);
            canvas.drawBitmap(texture, 0, 0, paint);

            return texture;
        }
        return null;
    }

    // 混合模式 -- 柔光
    private int softLight(int A, int B) {
        return (B < 128) ? (2 * ((A >> 1) + 64)) * (B / 255) : (255 - (2 * (255 - ((A >> 1) + 64)) * (255 - B) / 255));
    }

    // 混合模式 -- 叠加
    private int overlay(int A, int B) {
        return ((B < 128) ? (2 * A * B / 255) : (255 - 2 * (255 - A) * (255 - B) / 255));
    }

    public void getObjectAt(float x, float y) {
        if (mPicker != null) {
            mPicker.getObjectAt(x, y);
        }
    }

    public void changeOrnament(boolean isOrnamentChange) {
        mIsOrnamentChange = isOrnamentChange;
    }

    public void setOrnamentChangeListener(OrnamentChangeListener listener) {
        mOrnamentChangeListener = listener;
    }

    public static interface OrnamentChangeListener {
        public void onOrnamentChange();
    }

    //oldify_face_xx_obj
    /*private int[][] gvFacePts = new int[][]{
            {16, 41, 120, 179, 192, 236},
            {17, 186, 235, 245, 247},
            {64, 73, 238, 240, 252},
            {88, 131, 183, 363, 407, 460},
            {70, 118},
            {122, 193, 239},
            {85, 166},
            {8, 34, 74, 242},
            {35, 121, 178, 237, 241},
            {26, 27, 36, 135, 227, 266, 269},
            {87, 204, 260, 364, 481, 536},
            {2, 3, 47, 138, 233, 272, 275},
            {174, 195, 257},
            {141, 145, 156},
            {24, 126, 175, 226},
            {147, 151, 153},
            {169, 210},
            {81, 182, 209},
            {5, 57, 125, 132, 231, 551},
            {48, 53, 54, 324, 327, 332, 554, 557},
            {19, 31},
            {25, 128, 136, 218, 263},
            {144, 159, 211},
            {129, 185, 208},
            {139, 143, 146, 160, 271},
            {37, 148, 155, 172, 215, 265},
            {1, 28, 45, 230, 268},
            {38, 46, 161, 170, 171, 212, 267, 270},
            {89, 114, 130, 181, 205, 207},
            {15, 39, 188, 292, 316, 464},
            {127, 261},
            {22, 44, 94},
            {105, 202},
            {6, 9, 33, 40, 52, 84, 90, 102, 107, 167, 177, 198, 203, 224},
            {14, 68, 69},
            {189, 244, 248},
            {116, 119, 206, 258},
            {18, 23},
            {93, 559},
            {32, 67, 219},
            {108, 113, 137, 149, 152, 217, 264},
            {168, 173, 214},
            {12, 62, 71, 83, 115, 117, 180},
            {96, 101, 110, 150},
            {55, 58, 80, 134, 331, 356, 410},
            {60, 82, 250},
            {190, 465},
            {157, 163},
            {86, 103},
            {187, 191, 246, 462, 467, 523},
            {97, 109, 112},
            {100, 106, 222},
            {7, 11, 43, 72, 95, 253, 560},
            {49, 56, 79, 140, 142, 158, 162, 273},
            {154, 213},
            {29, 176, 196, 225, 229},
            {104, 201},
            {184, 459},
            {10, 92, 558},
            {63, 77, 251},
            {4, 78, 133, 274},
            {50, 164, 552},
            {},
            {165, 200},
            {194, 234, 243},
            {0, 124, 197, 228, 232, 255},
            {123, 256, 400, 531, 550},
            {91, 98, 99, 223},
            {51, 199, 553},
            {20, 21, 30, 42, 65, 75, 221, 254},
            {111, 216, 262},
            {13, 61, 66, 76, 220, 249},
            {259, 534},
            {291, 317, 397, 455, 469, 512},
            {293, 463, 510, 521, 522},
            {339, 348, 513, 517, 529},
            {345, 393},
            {398, 468, 515},
            {360, 441},
            {284, 309, 350, 518},
            {311, 396, 453, 514, 516},
            {302, 304, 313, 412, 503, 542, 545},
            {278, 280, 323, 335, 415, 509, 548},
            {451, 472, 533},
            {418, 420, 433},
            {301, 403, 450, 501},
            {424, 426, 430},
            {444, 487},
            {358, 458, 485},
            {59, 281, 401, 409, 508, 549},
            {294, 306},
            {300, 404, 411, 494, 539},
            {421, 436, 486},
            {406, 461, 483},
            {414, 419, 422, 435, 546},
            {312, 423, 431, 447, 491, 540},
            {276, 303, 322, 506, 543},
            {314, 321, 437, 446, 448, 488, 544, 547},
            {365, 391, 405, 456, 480, 484},
            {402, 538},
            {297, 320, 369},
            {382, 477},
            {283, 310, 315, 328, 361, 367, 379, 383, 443, 454, 475, 479, 500, 562},
            {290, 344, 346},
            {466, 519, 524},
            {392, 395, 482, 535},
            {295, 299},
            {285, 370},
            {308, 342, 496},
            {385, 389, 413, 425, 428, 492, 541},
            {445, 449, 489},
            {289, 338, 347, 359, 390, 394, 457},
            {373, 377, 386, 427},
            {337, 357, 525},
            {432, 438},
            {362, 378},
            {372, 384, 387},
            {375, 381, 499},
            {282, 287, 318, 349, 371, 528, 563},
            {326, 330, 334, 354, 416, 417, 434, 439},
            {429, 490},
            {305, 452, 471, 502, 504},
            {380, 478},
            {286, 368, 561},
            {340, 353, 527},
            {279, 333, 355, 408},
            {325, 440, 556},
            {442, 476},
            {470, 511, 520},
            {277, 399, 473, 505, 507, 532},
            {366, 374, 376, 498},
            {329, 474, 555},
            {296, 298, 307, 319, 341, 352, 497, 530},
            {388, 493, 537},
            {288, 336, 343, 351, 495, 526}
    };*/

    //oldify_face_new_obj
    private int[][] gvFacePts = new int[][]{
            {58, 62, 157},
            {156, 171, 223, 247},
            {101, 179, 250, 261, 265, 299, 710},
            {114, 140, 224, 246, 308, 707},
            {48, 59, 212, 219},
            {8, 24, 38, 43, 310},
            {21, 178, 297, 300, 662, 664},
            {3, 35, 40, 673, 677},
            {105, 111, 193, 319, 659},
            {0, 9, 28, 49, 211, 668},
            {14, 16, 110, 259, 267, 671},
            {2, 10, 100, 177, 665},
            {72, 87, 129, 235, 289, 291},
            {109, 153, 253, 260, 273, 281},
            {74, 94, 116, 138, 182, 236},
            {68, 187, 194, 254, 280, 318, 322},
            {78, 93, 135, 180, 215, 225, 403, 418, 460, 505, 539, 550},
            {112, 117, 184, 186, 192},
            {121, 149, 150},
            {144, 217, 230, 469, 540, 554},
            {5, 7, 85, 311, 675},
            {139, 181, 214, 222},
            {52, 160, 375, 483},
            {12, 15, 34, 45, 674},
            {27, 32, 33, 36, 39, 42, 46, 50, 54, 57, 60, 63, 71, 126, 141},
            {31, 51, 142, 354, 376, 465},
            {81, 118, 183, 189, 196},
            {55, 92, 128, 167, 169},
            {80, 146, 226, 228, 304},
            {90, 163, 168},
            {89, 272, 292},
            {204, 209, 220},
            {271, 287},
            {137, 213},
            {70, 127, 164, 170, 237},
            {202, 295},
            {73, 79, 95, 131, 305},
            {53, 69, 143, 159, 238},
            {91, 120, 165},
            {97, 284, 420, 608, 650, 653},
            {11, 99, 264, 268, 666, 670},
            {26, 30, 44, 98, 350, 355, 368, 422},
            {18, 23, 175, 206, 207, 660},
            {13, 29, 47, 667, 669},
            {130, 218, 229, 242, 288, 303},
            {405, 429, 437, 637, 644, 647},
            {61, 65, 147, 152, 158, 172},
            {4, 17, 86, 155, 258, 672},
            {6, 37, 41, 676},
            {145, 227, 468, 551},
            {83, 106, 113, 119, 656},
            {176, 201, 205},
            {104, 231, 278, 655},
            {245, 277, 296},
            {82, 102, 195, 255, 654},
            {77, 286, 698},
            {124, 313, 430, 606, 636, 649},
            {240, 290, 293},
            {136, 459},
            {216, 241, 541, 564},
            {151, 173, 248, 306, 697},
            {103, 232, 257, 263, 708},
            {25, 67, 84, 96, 154, 252, 282, 309},
            {191, 197, 198, 251, 256, 709},
            {1, 22, 208, 210, 221, 663},
            {56, 64, 122, 148, 166},
            {75, 88, 115, 234, 270, 285, 706},
            {185, 188, 190, 199, 275, 279},
            {19, 133, 174, 203, 244, 294},
            {20, 132, 301, 661},
            {107, 123, 406, 431, 657},
            {108, 200, 249, 266, 269, 274},
            {134, 233, 243, 262, 276, 298, 302},
            {125, 312, 320, 323, 658},
            {66, 283, 314, 321, 648},
            {381, 386, 480},
            {481, 496, 546, 570},
            {425, 503, 573, 585, 588, 623, 713},
            {439, 464, 548, 571, 632, 704},
            {373, 383, 536, 544},
            {332, 349, 362, 366, 633},
            {325, 346, 501, 622, 625},
            {328, 341, 359, 363, 683},
            {436, 448, 516, 642},
            {334, 351, 372, 534, 692, 694},
            {336, 434, 582, 592, 685, 689},
            {324, 423, 502, 691, 695},
            {397, 412, 454, 558, 612, 616},
            {432, 478, 576, 584, 598, 605},
            {398, 417, 440, 463, 506, 560},
            {392, 510, 518, 578, 603, 643, 645},
            {447, 507, 511, 517, 640},
            {444, 473, 475},
            {327, 330, 408, 635, 681},
            {462, 504, 537, 547},
            {340, 357, 370, 686, 688},
            {352, 356, 358, 361, 364, 367, 369, 374, 379, 382, 385, 388, 395, 451, 466},
            {316, 508, 514, 519, 639},
            {378, 416, 452, 491, 492},
            {404, 470, 549, 553, 627},
            {415, 486, 493},
            {413, 596, 615},
            {529, 533, 543},
            {594, 611},
            {461, 538},
            {393, 450, 488, 494, 562},
            {525, 618},
            {396, 402, 419, 455, 629},
            {377, 394, 467, 484, 561},
            {414, 445, 490},
            {333, 337, 424, 589, 591, 690},
            {344, 498, 530, 532, 679},
            {335, 338, 353, 371, 687},
            {453, 542, 552, 566, 613, 628},
            {161, 162, 239, 485, 487, 563},
            {384, 389, 472, 476, 482, 495},
            {339, 410, 479, 583, 682, 684},
            {329, 331, 360, 365},
            {317, 407, 435, 441, 449, 641},
            {500, 526, 528},
            {428, 442, 556, 602},
            {569, 600, 620},
            {315, 427, 443, 520, 580},
            {401, 609, 701},
            {565, 614, 617},
            {391, 607, 638, 646, 651},
            {399, 474, 497, 572, 703},
            {426, 555, 581, 587, 711},
            {348, 390, 409, 421, 477, 577, 634, 652},
            {515, 521, 523, 575, 579, 586},
            {326, 345, 531, 535, 545, 680, 693},
            {380, 387, 446, 471, 489},
            {411, 438, 559, 595, 610, 630, 700},
            {509, 512, 513, 522, 599, 604},
            {343, 456, 499, 527, 567, 619},
            {342, 347, 457, 624, 678},
            {433, 524, 574, 590, 593, 597},
            {458, 557, 568, 601, 621, 626, 712},
            {76, 307, 696, 705},
            {400, 631, 699, 702}
    };
}
